#include <map>
#include <string>
#include <assert.h>
#include "dynobj.hpp"


namespace dynobj
{

class ObjFactory
{
    typedef std::map<std::string, const RuntimeClass*> str2ptr;
    str2ptr _table;
public:
    void* create(const char* name)
    {
        str2ptr::iterator it = _table.find(name);
        if (it != _table.end())
            return it->second->create();
        return 0;
    }

    bool find(const char* name)
    {
        return _table.find(name) != _table.end();
    }

    void enum_class_name(void (*call)(void* ctx, const char* class_name), void* ctx)
    {
        if (ctx == 0)  
            return;
        for (str2ptr::iterator it=_table.begin(); it!=_table.end(); ++it)
        {
            call(ctx, it->first.c_str());
        }
    }

    void reg(const RuntimeClass* rc)
    {
        if (!rc) return;
        assert(!find(rc->name));
        _table[rc->name] = rc;
    }
    int size() { return _table.size(); }

    static ObjFactory* instance()
    {
        static ObjFactory* ptr = new ObjFactory;
        return ptr;
    }
};

namespace internal
{
ClassReg::ClassReg(const RuntimeClass* rc)
{
    ObjFactory::instance()->reg(rc);
}
}

void* create_dynobj(const char* class_name)
{
    int a = ObjFactory::instance()->size();
    a ++;
    return ObjFactory::instance()->create(class_name);
}

bool find_class(const char* class_name)
{
    return ObjFactory::instance()->find(class_name);
}

void enum_class(void (*call)(void* ctx, const char* class_name), void* ctx)
{
    return ObjFactory::instance()->enum_class_name(call, ctx);
}

}

//#define _DYNOBJ_TEST

#ifdef _DYNOBJ_TEST

#include <stdio.h>

using namespace dynobj;

class DemoA
{
public:
    virtual void test()
    {
        printf("DemoA\n");
    }
    DEF_DYNOBJ(DemoA)
};

IMP_DYNOBJ(DemoA)


class DemoB
{
public:
    virtual void test()
    {
        printf("DemoB\n");
    }
    DEF_DYNOBJ(DemoB)
};

IMP_DYNOBJ(DemoB)

int main()
{
    DemoA* a = (DemoA*)create_dynobj("DemoA");
    printf("class: %s\n", a->get_runtimeclass()->name);
    a->test();

    a = (DemoA*)create_dynobj("DemoB");
    printf("class: %s\n", a->get_runtimeclass()->name);
    a->test();

    return 0;
}

#endif  //_DYNOBJ_TEST
